#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <jsoncpp/json/json.h>

#define SEP " "
#define SEP_LEN strlen(SEP) // 不能用 sizeof() 的哦
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP) // 不能用 sizeof() 的哦

enum
{
    OK = 0,
    DIV_ZERO,
    MOD_ZERO,
    OP_ZERO
};

// "x op y" -> "content_len"\r\n"x op y"\r\n
// "exitcode result" -> "content_len"\r\n"exitcode result"\r\n
// 添加报文报头和 LINE_SEP
std::string enLength(const std::string &text)
{
    std::string send_string = std::to_string(text.size());
    send_string += LINE_SEP;
    send_string += text;
    send_string += LINE_SEP;

    return send_string;
}

// "content_len"\r\n"exitcode result"\r\n -> "exitcode result"
// 去掉报头
bool deLength(const std::string &package, std::string *text)
{
    auto pos = package.find(LINE_SEP);
    if (pos == std::string::npos)
        return false;
    std::string text_len_string = package.substr(0, pos);
    int text_len = std::stoi(text_len_string);
    *text = package.substr(pos + LINE_SEP_LEN, text_len);
    return true;
}

// 没有人规定我们网络通信的时候，只能有一种协议！！
// 我们怎么让系统知道我们用的是哪一种协议呢？？
// "content_len"\r\n"协议编号"\r\n"x op y"\r\n

// 请求计算的 class
class Request
{
public:
    Request() : x(0), y(0), op(0) {}
    Request(int x_, int y_, char op_) : x(x_), y(y_), op(op_) {}

    // 1. 自己写的
    // 2. 用现成的

    // 序列化
    bool serialize(std::string *out)
    {
        *out = "";
        // 结构化的数据 -> "x op y"
        std::string x_string = std::to_string(x);
        std::string y_string = std::to_string(y);

        *out = x_string;
        *out += SEP;
        *out += op; // char 类型是可以转化为 string 类型的哦
        *out += SEP;
        *out += y_string;

        return true;
    }

    // 反序列化
    bool deserialize(const std::string &in)
    {
        // "x op y" -> 结构化的数据
        auto left = in.find(SEP);
        auto right = in.find(SEP);
        if (left == std::string::npos || right == std::string::npos)
            return false;
        if (left == right)
            return false; // 一定是得有两个 SEP 分隔符的哦
        if (right - (left + SEP_LEN) != 1)
            return false;

        std::string x_string = in.substr(0, left); // [left, right)
        std::string y_string = in.substr(right + SEP_LEN);

        if (x_string.empty() == true)
            return false;
        if (y_string.empty() == true)
            return false;

        x = std::stoi(x_string);
        y = std::stoi(y_string);
        op = in[left + SEP_LEN];

        return true;
    }

    ~Request() {}

    // private:
    int x;
    int y;
    char op;
};

// 返回响应的 class
class Response
{
public:
    Response() : exitcode(0), result(0) {}
    Response(int exitcode_, int result_) : exitcode(exitcode_), result(result_) {}

    // 1. 自己写的
    // 2. 用现成的

    // 序列化
    bool serialize(std::string *out)
    {
        // int exitcode, result -> "exitcode result"
        *out = "";
        std::string ec_string = std::to_string(exitcode);
        std::string res_string = std::to_string(result);

        *out = ec_string;
        *out += SEP;
        *out += res_string;

        return true;
    }

    // 反序列化
    bool deserialize(const std::string &in)
    {
        // "exitcode result" -> int exitcode, result;
        auto mid = in.find(SEP);
        if (mid == std::string::npos)
            return false;
        std::string ec_string = in.substr(0, mid);
        std::string res_string = in.substr(mid + SEP_LEN);
        if (ec_string.empty() == true || res_string.empty() == true)
            return false;

        exitcode = std::stoi(ec_string);
        result = std::stoi(res_string);

        return true;
    }

    ~Response() {}

    // private:
    int exitcode; // 0：计算成功，!0 表示计算失败，具体是多少，定好标准
    int result;   // 计算结果
};

// 每一次读取一个报文
// "content_len"\r\n"x op y"\r\n"content_len"\r\n"x op y"\r\n"content_len"\r\n"x op
bool recvPackage(int sock, std::string &inbuffer, std::string *text)
{
    // inbuffer 是外面传递进来的一个类似于缓冲区的作用的字符串
    // 使得 inbuffer 字符串至少读取了一个报文，text 就从 inbuffer 里面截取一个报文就可以了
    char buffer[1024];
    while (true)
    {
        // 用 recv 函数和用 read 函数没有很大的区别的哦
        ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer += buffer;
            // 分析处理
            auto pos = inbuffer.find(LINE_SEP);
            // 说明读入的还不够一个报文，因为 tcpsocket 是面向字节流的可能读的多可能读的少了
            if (pos == std::string::npos)
                continue;

            // 说明 content_len 一定读取进来了
            std::string text_len_string = inbuffer.substr(0, pos);
            int text_len = std::stoi(text_len_string);
            int total_len = text_len_string.size() + 2 * LINE_SEP_LEN + text_len;

            // text_len_string + "\r\n" + text + "\r\n" <= inbuffer.size()
            // std::cout << "处理前#inbuffer: \n" << inbuffer << std::endl;
            if (inbuffer.size() < total_len)
            {
                // std::cout << "你输入的信息，没有严格按照我们的协议，正在等待后续的内容，continue" << std::endl;
                continue;
            }

            // 至少有一个完整的报文了
            *text = inbuffer.substr(0, total_len);
            inbuffer.erase(0, total_len);

            // std::cout << "处理后#inbuffer:\n" << inbuffer << std::endl;
        }
        else
            return false;
    }

    return true;
}

// 都取所有的报文
// bool recvRequestAll(int sock, std::vector<std::string> *out){
//     std::string line;
//     while(recvPackage(sock, &line)) out->push_back(line);
// }
